"""
增强版Kubernetes资源验证器
"""
from typing import Dict, List, Any, Optional, Tuple
import re
from dataclasses import dataclass
import jsonschema
from kubernetes import client
from kubernetes.client.rest import ApiException

@dataclass
class ValidationResult:
    """验证结果"""
    is_valid: bool
    errors: List[str]
    warnings: List[str]
    suggestions: List[str]

class EnhancedResourceValidator:
    """增强版资源验证器"""
    
    def __init__(self):
        self._schema_cache = {}
        self._k8s_api = client.ApiClient()
        
    def validate_resource(self, resource: Dict[str, Any]) -> ValidationResult:
        """
        验证Kubernetes资源
        
        Args:
            resource: 要验证的资源定义
            
        Returns:
            ValidationResult: 验证结果
        """
        errors = []
        warnings = []
        suggestions = []
        
        # 1. 基本结构验证
        if not self._validate_basic_structure(resource, errors):
            return ValidationResult(False, errors, warnings, suggestions)
            
        # 2. 版本兼容性验证
        self._validate_version_compatibility(resource, errors, warnings)
        
        # 3. 字段格式验证
        self._validate_field_formats(resource, errors, warnings)
        
        # 4. 资源限制验证
        self._validate_resource_limits(resource, errors, warnings)
        
        # 5. 标签和注解验证
        self._validate_labels_annotations(resource, errors, warnings)
        
        # 6. 端口验证
        self._validate_ports(resource, errors, warnings)
        
        # 7. 卷挂载验证
        self._validate_volume_mounts(resource, errors, warnings)
        
        # 8. 环境变量验证
        self._validate_env_vars(resource, errors, warnings)
        
        # 9. 健康检查验证
        self._validate_health_checks(resource, errors, warnings)
        
        # 10. 安全上下文验证
        self._validate_security_context(resource, errors, warnings)
        
        # 11. 网络策略验证
        self._validate_network_policy(resource, errors, warnings)
        
        # 12. 存储验证
        self._validate_storage(resource, errors, warnings)
        
        # 13. 资源引用验证
        self._validate_resource_references(resource, errors, warnings)
        
        # 14. 生成优化建议
        self._generate_optimization_suggestions(resource, suggestions)
        
        return ValidationResult(
            is_valid=len(errors) == 0,
            errors=errors,
            warnings=warnings,
            suggestions=suggestions
        )
    
    def _validate_basic_structure(self, resource: Dict[str, Any], errors: List[str]) -> bool:
        """验证资源基本结构"""
        required_fields = ["apiVersion", "kind", "metadata"]
        for field in required_fields:
            if field not in resource:
                errors.append(f"缺少必需字段: {field}")
                return False
        return True
    
    def _validate_version_compatibility(self, resource: Dict[str, Any], errors: List[str], warnings: List[str]):
        """验证版本兼容性"""
        try:
            api_version = resource["apiVersion"]
            kind = resource["kind"]
            
            # 检查API版本是否支持
            if not self._is_api_version_supported(api_version):
                warnings.append(f"API版本 {api_version} 可能不是最新版本")
                
            # 检查资源类型是否支持该API版本
            if not self._is_kind_supported_in_version(kind, api_version):
                errors.append(f"资源类型 {kind} 在API版本 {api_version} 中不支持")
                
        except Exception as e:
            errors.append(f"版本兼容性验证失败: {str(e)}")
    
    def _validate_field_formats(self, resource: Dict[str, Any], errors: List[str], warnings: List[str]):
        """验证字段格式"""
        # 验证名称格式
        if "metadata" in resource and "name" in resource["metadata"]:
            name = resource["metadata"]["name"]
            if not re.match(r'^[a-z0-9]([-a-z0-9]*[a-z0-9])?$', name):
                errors.append(f"资源名称 '{name}' 格式不正确")
        
        # 验证命名空间格式
        if "metadata" in resource and "namespace" in resource["metadata"]:
            namespace = resource["metadata"]["namespace"]
            if not re.match(r'^[a-z0-9]([-a-z0-9]*[a-z0-9])?$', namespace):
                errors.append(f"命名空间 '{namespace}' 格式不正确")
    
    def _validate_resource_limits(self, resource: Dict[str, Any], errors: List[str], warnings: List[str]):
        """验证资源限制"""
        if "spec" in resource and "template" in resource["spec"]:
            containers = resource["spec"]["template"]["spec"].get("containers", [])
            for container in containers:
                if "resources" in container:
                    resources = container["resources"]
                    
                    # 验证CPU限制
                    if "limits" in resources and "cpu" in resources["limits"]:
                        cpu_limit = resources["limits"]["cpu"]
                        if not self._is_valid_cpu_value(cpu_limit):
                            errors.append(f"CPU限制值 '{cpu_limit}' 格式不正确")
                    
                    # 验证内存限制
                    if "limits" in resources and "memory" in resources["limits"]:
                        memory_limit = resources["limits"]["memory"]
                        if not self._is_valid_memory_value(memory_limit):
                            errors.append(f"内存限制值 '{memory_limit}' 格式不正确")
    
    def _validate_labels_annotations(self, resource: Dict[str, Any], errors: List[str], warnings: List[str]):
        """验证标签和注解"""
        if "metadata" in resource:
            # 验证标签
            if "labels" in resource["metadata"]:
                labels = resource["metadata"]["labels"]
                for key, value in labels.items():
                    if not self._is_valid_label_key(key):
                        errors.append(f"标签键 '{key}' 格式不正确")
                    if not self._is_valid_label_value(value):
                        errors.append(f"标签值 '{value}' 格式不正确")
            
            # 验证注解
            if "annotations" in resource["metadata"]:
                annotations = resource["metadata"]["annotations"]
                for key, value in annotations.items():
                    if not self._is_valid_annotation_key(key):
                        errors.append(f"注解键 '{key}' 格式不正确")
    
    def _validate_ports(self, resource: Dict[str, Any], errors: List[str], warnings: List[str]):
        """验证端口配置"""
        if "spec" in resource and "template" in resource["spec"]:
            containers = resource["spec"]["template"]["spec"].get("containers", [])
            for container in containers:
                if "ports" in container:
                    for port in container["ports"]:
                        # 验证端口号
                        if "containerPort" in port:
                            port_num = port["containerPort"]
                            if not isinstance(port_num, int) or port_num < 1 or port_num > 65535:
                                errors.append(f"容器端口号 {port_num} 无效")
                        
                        # 验证协议
                        if "protocol" in port:
                            protocol = port["protocol"]
                            if protocol not in ["TCP", "UDP", "SCTP"]:
                                errors.append(f"端口协议 {protocol} 无效")
    
    def _validate_volume_mounts(self, resource: Dict[str, Any], errors: List[str], warnings: List[str]):
        """验证卷挂载配置"""
        if "spec" in resource and "template" in resource["spec"]:
            containers = resource["spec"]["template"]["spec"].get("containers", [])
            volumes = resource["spec"]["template"]["spec"].get("volumes", [])
            
            # 验证卷挂载
            for container in containers:
                if "volumeMounts" in container:
                    for mount in container["volumeMounts"]:
                        # 验证挂载点
                        if "mountPath" in mount:
                            mount_path = mount["mountPath"]
                            if not mount_path.startswith("/"):
                                errors.append(f"挂载点 '{mount_path}' 必须是绝对路径")
                        
                        # 验证卷名称
                        if "name" in mount:
                            volume_name = mount["name"]
                            if not any(v["name"] == volume_name for v in volumes):
                                errors.append(f"卷 '{volume_name}' 未定义")
    
    def _validate_env_vars(self, resource: Dict[str, Any], errors: List[str], warnings: List[str]):
        """验证环境变量配置"""
        if "spec" in resource and "template" in resource["spec"]:
            containers = resource["spec"]["template"]["spec"].get("containers", [])
            for container in containers:
                if "env" in container:
                    for env in container["env"]:
                        # 验证环境变量名称
                        if "name" in env:
                            name = env["name"]
                            if not re.match(r'^[A-Za-z_][A-Za-z0-9_]*$', name):
                                errors.append(f"环境变量名称 '{name}' 格式不正确")
    
    def _validate_health_checks(self, resource: Dict[str, Any], errors: List[str], warnings: List[str]):
        """验证健康检查配置"""
        if "spec" in resource and "template" in resource["spec"]:
            containers = resource["spec"]["template"]["spec"].get("containers", [])
            for container in containers:
                # 验证就绪探针
                if "readinessProbe" in container:
                    probe = container["readinessProbe"]
                    self._validate_probe(probe, "就绪探针", errors, warnings)
                
                # 验证存活探针
                if "livenessProbe" in container:
                    probe = container["livenessProbe"]
                    self._validate_probe(probe, "存活探针", errors, warnings)
                
                # 验证启动探针
                if "startupProbe" in container:
                    probe = container["startupProbe"]
                    self._validate_probe(probe, "启动探针", errors, warnings)
    
    def _validate_probe(self, probe: Dict[str, Any], probe_type: str, errors: List[str], warnings: List[str]):
        """验证探针配置"""
        # 验证超时时间
        if "timeoutSeconds" in probe:
            timeout = probe["timeoutSeconds"]
            if not isinstance(timeout, int) or timeout < 1:
                errors.append(f"{probe_type}超时时间 {timeout} 无效")
        
        # 验证间隔时间
        if "periodSeconds" in probe:
            period = probe["periodSeconds"]
            if not isinstance(period, int) or period < 1:
                errors.append(f"{probe_type}间隔时间 {period} 无效")
        
        # 验证重试次数
        if "failureThreshold" in probe:
            threshold = probe["failureThreshold"]
            if not isinstance(threshold, int) or threshold < 1:
                errors.append(f"{probe_type}重试次数 {threshold} 无效")
    
    def _validate_security_context(self, resource: Dict[str, Any], errors: List[str], warnings: List[str]):
        """验证安全上下文配置"""
        if "spec" in resource and "template" in resource["spec"]:
            spec = resource["spec"]["template"]["spec"]
            
            # 验证Pod安全上下文
            if "securityContext" in spec:
                context = spec["securityContext"]
                self._validate_pod_security_context(context, errors, warnings)
            
            # 验证容器安全上下文
            containers = spec.get("containers", [])
            for container in containers:
                if "securityContext" in container:
                    context = container["securityContext"]
                    self._validate_container_security_context(context, errors, warnings)
    
    def _validate_pod_security_context(self, context: Dict[str, Any], errors: List[str], warnings: List[str]):
        """验证Pod安全上下文"""
        # 验证运行用户
        if "runAsUser" in context:
            user = context["runAsUser"]
            if not isinstance(user, int) or user < 0:
                errors.append(f"运行用户ID {user} 无效")
        
        # 验证运行组
        if "runAsGroup" in context:
            group = context["runAsGroup"]
            if not isinstance(group, int) or group < 0:
                errors.append(f"运行组ID {group} 无效")
    
    def _validate_container_security_context(self, context: Dict[str, Any], errors: List[str], warnings: List[str]):
        """验证容器安全上下文"""
        # 验证特权模式
        if "privileged" in context and context["privileged"]:
            warnings.append("容器以特权模式运行")
        
        # 验证只读根文件系统
        if "readOnlyRootFilesystem" in context and context["readOnlyRootFilesystem"]:
            warnings.append("容器根文件系统为只读")
    
    def _validate_network_policy(self, resource: Dict[str, Any], errors: List[str], warnings: List[str]):
        """验证网络策略配置"""
        if resource["kind"] == "NetworkPolicy":
            spec = resource.get("spec", {})
            
            # 验证Pod选择器
            if "podSelector" in spec:
                self._validate_label_selector(spec["podSelector"], "Pod选择器", errors, warnings)
            
            # 验证入站规则
            if "ingress" in spec:
                for rule in spec["ingress"]:
                    self._validate_network_policy_rule(rule, "入站规则", errors, warnings)
            
            # 验证出站规则
            if "egress" in spec:
                for rule in spec["egress"]:
                    self._validate_network_policy_rule(rule, "出站规则", errors, warnings)
    
    def _validate_network_policy_rule(self, rule: Dict[str, Any], rule_type: str, errors: List[str], warnings: List[str]):
        """验证网络策略规则"""
        # 验证端口
        if "ports" in rule:
            for port in rule["ports"]:
                if "port" in port:
                    port_num = port["port"]
                    if not isinstance(port_num, int) or port_num < 1 or port_num > 65535:
                        errors.append(f"{rule_type}端口号 {port_num} 无效")
    
    def _validate_storage(self, resource: Dict[str, Any], errors: List[str], warnings: List[str]):
        """验证存储配置"""
        if "spec" in resource and "template" in resource["spec"]:
            volumes = resource["spec"]["template"]["spec"].get("volumes", [])
            for volume in volumes:
                # 验证持久卷声明
                if "persistentVolumeClaim" in volume:
                    pvc = volume["persistentVolumeClaim"]
                    if "claimName" not in pvc:
                        errors.append(f"持久卷声明缺少claimName字段")
                
                # 验证配置映射
                if "configMap" in volume:
                    config_map = volume["configMap"]
                    if "name" not in config_map:
                        errors.append(f"配置映射缺少name字段")
                
                # 验证密钥
                if "secret" in volume:
                    secret = volume["secret"]
                    if "secretName" not in secret:
                        errors.append(f"密钥缺少secretName字段")
    
    def _validate_resource_references(self, resource: Dict[str, Any], errors: List[str], warnings: List[str]):
        """验证资源引用"""
        if "spec" in resource and "template" in resource["spec"]:
            containers = resource["spec"]["template"]["spec"].get("containers", [])
            for container in containers:
                # 验证环境变量引用
                if "env" in container:
                    for env in container["env"]:
                        if "valueFrom" in env:
                            value_from = env["valueFrom"]
                            self._validate_value_from(value_from, errors, warnings)
    
    def _validate_value_from(self, value_from: Dict[str, Any], errors: List[str], warnings: List[str]):
        """验证值来源配置"""
        # 验证配置映射引用
        if "configMapKeyRef" in value_from:
            ref = value_from["configMapKeyRef"]
            if "name" not in ref or "key" not in ref:
                errors.append("配置映射引用缺少必要字段")
        
        # 验证密钥引用
        if "secretKeyRef" in value_from:
            ref = value_from["secretKeyRef"]
            if "name" not in ref or "key" not in ref:
                errors.append("密钥引用缺少必要字段")
    
    def _generate_optimization_suggestions(self, resource: Dict[str, Any], suggestions: List[str]):
        """生成优化建议"""
        if "spec" in resource and "template" in resource["spec"]:
            containers = resource["spec"]["template"]["spec"].get("containers", [])
            for container in containers:
                # 检查资源限制
                if "resources" in container:
                    resources = container["resources"]
                    if "requests" not in resources:
                        suggestions.append("建议设置资源请求以优化调度")
                    if "limits" not in resources:
                        suggestions.append("建议设置资源限制以防止资源耗尽")
                
                # 检查健康检查
                if "livenessProbe" not in container:
                    suggestions.append("建议配置存活探针以确保容器健康")
                if "readinessProbe" not in container:
                    suggestions.append("建议配置就绪探针以确保流量路由正确")
                
                # 检查安全上下文
                if "securityContext" not in container:
                    suggestions.append("建议配置安全上下文以增强安全性")
    
    def _is_valid_cpu_value(self, value: str) -> bool:
        """验证CPU值格式"""
        if isinstance(value, (int, float)):
            return value > 0
        if isinstance(value, str):
            return bool(re.match(r'^[0-9]+m?$', value))
        return False
    
    def _is_valid_memory_value(self, value: str) -> bool:
        """验证内存值格式"""
        if isinstance(value, (int, float)):
            return value > 0
        if isinstance(value, str):
            return bool(re.match(r'^[0-9]+[KMGTPEZYkmgtpezy]i?$', value))
        return False
    
    def _is_valid_label_key(self, key: str) -> bool:
        """验证标签键格式"""
        return bool(re.match(r'^([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$', key))
    
    def _is_valid_label_value(self, value: str) -> bool:
        """验证标签值格式"""
        return bool(re.match(r'^([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$', value))
    
    def _is_valid_annotation_key(self, key: str) -> bool:
        """验证注解键格式"""
        return bool(re.match(r'^([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9](/[A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$', key))
    
    def _is_api_version_supported(self, api_version: str) -> bool:
        """检查API版本是否支持"""
        try:
            self._k8s_api.resources.get_api_resources(api_version)
            return True
        except ApiException:
            return False
    
    def _is_kind_supported_in_version(self, kind: str, api_version: str) -> bool:
        """检查资源类型是否支持该API版本"""
        try:
            resources = self._k8s_api.resources.get_api_resources(api_version)
            return any(r.kind == kind for r in resources)
        except ApiException:
            return False 